package org.opens.sql.genHandle.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import org.opens.db.JdbcUtil;
import org.opens.db.column.entity.ColumnEntity;
import org.opens.db.column.entity.TableEntity;
import org.opens.db.column.util.DbConfigUtil;
import org.opens.db.column.service.I_DbInfoService;
import org.opens.db.column.service.DbInfoServiceFactory;
import org.opens.exception.BusinessException;
import org.opens.freemarker.FreemarkerUtil;
import org.opens.sql.GenCodeConfigUtil;
import org.opens.sql.constant.DeleteMode;
import org.opens.sql.constant.GenMapperMode;
import org.opens.sql.constant.GenProjectPath;
import org.opens.sql.constant.TombstoneColumnType;
import org.opens.sql.genConfig.GenSqlConfig;
import org.opens.sql.genConfig.TableConfig;
import org.opens.sql.genConfig.TombstoneConfig;
import org.opens.sql.genHandle.GenHandle;
import org.opens.util.DatabaseConnectInfoEntity;
import org.opens.util.FileUtil;

import java.io.File;
import java.util.*;

/**
 * 控制生成mapper代码
 */
public class GenMapperHandle implements GenHandle {

    private static final String mapperFtlFilePath = "JavaMapper.ftl";

    private static final String daoFtlFilePath = "JavaDao.ftl";

    @Override
    public void genCode(GenSqlConfig genSqlConfig) {
        // 数据库连接配置
        DatabaseConnectInfoEntity databaseConnectInfo = genSqlConfig.getDatabaseConnectInfo();

        // 从数据库中获取column信息
        I_DbInfoService IDbInfoService = DbInfoServiceFactory.getColumnServiceInstance(databaseConnectInfo.getDbType());

        // 获取表信息
        TableEntity tableInfo = IDbInfoService.getTableInfo(databaseConnectInfo);

        // 所有的列
        List<ColumnEntity> columnEntityList = IDbInfoService.getAllColumn(databaseConnectInfo);

        // 获取当前表配置
        TableConfig currentTableConfig = GenCodeConfigUtil.checkCurrentTableConfig(genSqlConfig.getTableConfigs(), tableInfo.getTableName());

        // 删除需要忽略的列
        if (currentTableConfig != null) {
            DbConfigUtil.removeIgnoreColumn(columnEntityList, currentTableConfig.getIgnoreColumns());
        }

        // 自动设置java类型
        DbConfigUtil.autoSetColumnConfig(genSqlConfig.getDatabaseConnectInfo().getDbType(), columnEntityList);

        String tableName = databaseConnectInfo.getTableName();

        // 获取表的别名
        String tableAlias = JdbcUtil.calcTableAlias(tableName);

        // 计算出需要导入的class信息
        Set<String> importSet = calcMapperImportList(genSqlConfig);

        ColumnEntity pkEntity = null;
        for (ColumnEntity columnEntityTemp : columnEntityList) {
            if ("PRI".equals(columnEntityTemp.getColumnKey())) {
                pkEntity = columnEntityTemp;
                break;
            }
        }

        String entityImportInfo = genSqlConfig.getGenEntityPublicConfig().getPackageInfo() + "." + StrUtil.upperFirst(StrUtil.toCamelCase(tableName)) + "Entity";

        Map<String, Object> entityPublicConfig = BeanUtil.beanToMap(genSqlConfig.getGenMapperPublicConfig());

        Map<String, Object> ftlMap = new HashMap<>();
        ftlMap.putAll(entityPublicConfig);
        ftlMap.put("columnEntityList", columnEntityList);
        ftlMap.put("tableName", databaseConnectInfo.getTableName());
        ftlMap.put("genMapperPublicConfig", genSqlConfig.getGenMapperPublicConfig());
        ftlMap.put("entityName", StrUtil.upperFirst(StrUtil.toCamelCase(tableName)) + "Entity");
        ftlMap.put("daoName", StrUtil.upperFirst(StrUtil.toCamelCase(tableName)) + "Dao");
        ftlMap.put("tableInfo", tableInfo);
        ftlMap.put("tableAlias", tableAlias);
        ftlMap.put("entityImportInfo", entityImportInfo);
        ftlMap.put("daoImportInfo", genSqlConfig.getGenMapperPublicConfig().getDaoPackage() + "." + StrUtil.upperFirst(StrUtil.toCamelCase(tableName)) + "Dao");
        ftlMap.put("daoPackage", genSqlConfig.getGenMapperPublicConfig().getDaoPackage());
        ftlMap.put("authorInfo", genSqlConfig.getAuthorInfo());
        ftlMap.put("importSet", importSet);
        ftlMap.put("pkEntity", pkEntity);
        ftlMap.put("dbType", databaseConnectInfo.getDbType().getTypeName());
        ftlMap.put("genMapperMode", genSqlConfig.getGenMapperPublicConfig().getGenMapperMode().getValue());
        ftlMap.put("idType", genSqlConfig.getGenMapperPublicConfig().getIdType());
        ftlMap.put("sequenceName", genSqlConfig.getGenMapperPublicConfig().getSequenceName());
        ftlMap.put("currentTableConfig", currentTableConfig);
        ftlMap.put("deleteMode", genSqlConfig.getGenMapperPublicConfig().getDeleteMode());
        ftlMap.put("tombstoneConfig", genSqlConfig.getGenMapperPublicConfig().getTombstoneConfig());

        genDao(genSqlConfig, ftlMap);
        genMapper(genSqlConfig, ftlMap);
    }

    private void genDao(GenSqlConfig genSqlConfig, Map<String, Object> ftlMap) {
        // 将包的包含.的路径转换为真实路径
        String daoPath = StrUtil.replace(genSqlConfig.getGenMapperPublicConfig().getDaoPackage(), ".", File.separator);
        // 父目录
        String descParentPath = FileUtil.pathSplicing(genSqlConfig.getGenPath(), GenProjectPath.MAVEN_PROJECT_PREFIX, daoPath);
        // java文件全路径
        String descPath = FileUtil.pathSplicing(descParentPath, StrUtil.upperFirst(StrUtil.toCamelCase(genSqlConfig.getDatabaseConnectInfo().getTableName())) + "Dao.java");

        // 创建目录
        FileUtil.createFolder(descParentPath);

        FreemarkerUtil.compileProcess(descPath, ftlMap, daoFtlFilePath);
    }

    private void genMapper(GenSqlConfig genSqlConfig, Map<String, Object> ftlMap) {
        // 将包的包含.的路径转换为真实路径
        String resourcesPath = StrUtil.replace(genSqlConfig.getGenMapperPublicConfig().getResourcePath(), ".", File.separator);
        // 父目录
        String descParentPath = FileUtil.pathSplicing(genSqlConfig.getGenPath(), GenProjectPath.MAVEN_PROJECT_RESOURCE_PREFIX, resourcesPath);
        // java文件全路径
        String descPath = FileUtil.pathSplicing(descParentPath, StrUtil.upperFirst(StrUtil.toCamelCase(genSqlConfig.getDatabaseConnectInfo().getTableName())) + "Mapper.xml");

        // 创建目录
        FileUtil.createFolder(descParentPath);

        FreemarkerUtil.compileProcess(descPath, ftlMap, mapperFtlFilePath);
    }

    @Override
    public void genCodeValid(GenSqlConfig genSqlConfig) {
        if (genSqlConfig.getGenMapperPublicConfig().getGenMapperMode() == null) {
            throw new IllegalArgumentException("生成mapper公共配置异常: [genMapperMode]不能为空!");
        }

        if (StrUtil.isBlank(genSqlConfig.getGenEntityPublicConfig().getPackageInfo())) {
            throw new BusinessException("未配置实体包名, mapper终止生成!");
        }

        // 逻辑删除的已删除和未删除的值的验证
        if (genSqlConfig.getGenMapperPublicConfig().getDeleteMode() == DeleteMode.TOMBSTONE) {
            TombstoneConfig tombstoneConfig = genSqlConfig.getGenMapperPublicConfig().getTombstoneConfig();
            if (tombstoneConfig.getTombstoneColumnType() == TombstoneColumnType.CHAR) {
                if (StrUtil.isBlank(tombstoneConfig.getDeletedValue())) {
                    throw new BusinessException("逻辑删除的已删除值不能为空!");
                }
                if (StrUtil.isBlank(tombstoneConfig.getNotDeletedValue())) {
                    throw new BusinessException("逻辑删除的未删除值不能为空!");
                }
                if (tombstoneConfig.getDeletedValue().length() != 1) {
                    throw new IllegalArgumentException("逻辑删除的已删除值的长度只能为1");
                }
                if (tombstoneConfig.getNotDeletedValue().length() != 1) {
                    throw new IllegalArgumentException("逻辑删除的未删除值的长度只能为1");
                }
            }
        }
    }

    /**
     * 计算生成mapper需要导入的类
     * @param genSqlConfig 生成配置
     * @return 需要导入的java类信息
     */
    private Set<String> calcMapperImportList(GenSqlConfig genSqlConfig) {
        // 数据库连接配置
        DatabaseConnectInfoEntity databaseConnectInfo = genSqlConfig.getDatabaseConnectInfo();

        String tableName = databaseConnectInfo.getTableName();

        // 计算出需要导入的class信息
        Set<String> importSet = new HashSet<>();
        String entityImportInfo = genSqlConfig.getGenEntityPublicConfig().getPackageInfo() + "." + StrUtil.upperFirst(StrUtil.toCamelCase(tableName)) + "Entity";

        importSet.add(entityImportInfo);
        importSet.add("org.apache.ibatis.annotations.Mapper");
        importSet.add("org.apache.ibatis.annotations.Param");
        importSet.add("java.io.Serializable");
        importSet.add("java.util.Collection");

        // 批量查找用到了这个类
        importSet.add("java.util.List");

        if (GenMapperMode.MYBATIS_PLUS.equals(genSqlConfig.getGenMapperPublicConfig().getGenMapperMode())) {
            importSet.add("com.baomidou.mybatisplus.core.mapper.BaseMapper");
        }

        return importSet;
    }

}
